package org.zezi.loader.provider;

import java.util.HashMap;

import org.zezi.loader.provider.ShopListContract.Items;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

public class ShopListProvider extends ContentProvider {

	private static final String TAG = "ShopListProvider";

    /**
     * The database that the provider uses as its underlying data store
     */
    private static final String DATABASE_NAME = "shop_list.db";

    /**
     * The database version
     */
    private static final int DATABASE_VERSION = 2;

    /**
     * A projection map used to select columns from the database
     */
    private static HashMap<String, String> sItemsProjectionMap;

    /**
     * Standard projection for the interesting columns.
     */
    private static final String[] READ_ITEM_PROJECTION = new String[] {
            ShopListContract.Items._ID,               // Projection position 0, the note's id
            ShopListContract.Items.COLUMN_NAME_NAME,  // Projection position 1, the note's content
            ShopListContract.Items.COLUMN_NAME_COUNT, // Projection position 2, the note's title
    };
//    private static final int READ_NOTE_NOTE_INDEX = 1;
//    private static final int READ_NOTE_TITLE_INDEX = 2;

    /*
     * Constants used by the Uri matcher to choose an action based on the pattern
     * of the incoming URI
     */
    // The incoming URI matches the Items URI pattern
    private static final int ITEMS = 1;

    // The incoming URI matches the Item ID URI pattern
    private static final int ITEM_ID = 2;
    
    private static final int FILTER = 3;

    /**
     * A UriMatcher instance
     */
    private static final UriMatcher sUriMatcher;

    // Handle to a new DatabaseHelper.
    private DatabaseHelper mOpenHelper;
    
    /**
     * A block that instantiates and sets static objects
     */
    static {

        /*
         * Creates and initializes the URI matcher
         */
        // Create a new instance
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

        // Add a pattern that routes URIs terminated with "items" to a NOTES operation
        sUriMatcher.addURI(ShopListContract.AUTHORITY, "items", ITEMS);

        // Add a pattern that routes URIs terminated with "notes" plus an integer
        // to a note ID operation
        sUriMatcher.addURI(ShopListContract.AUTHORITY, "items/#", ITEM_ID);
        
        sUriMatcher.addURI(ShopListContract.AUTHORITY, "items/filter", FILTER);
        sUriMatcher.addURI(ShopListContract.AUTHORITY, "items/filter/*", FILTER);

        /*
         * Creates and initializes a projection map that returns all columns
         */

        // Creates a new projection map instance. The map returns a column name
        // given a string. The two are usually equal.
        sItemsProjectionMap = new HashMap<String, String>();

        // Maps the string "_ID" to the column name "_ID"
        sItemsProjectionMap.put(ShopListContract.Items._ID, ShopListContract.Items._ID);

        // Maps "name" to "name"
        sItemsProjectionMap.put(ShopListContract.Items.COLUMN_NAME_NAME, ShopListContract.Items.COLUMN_NAME_NAME);

        // Maps "count" to "count"
        sItemsProjectionMap.put(ShopListContract.Items.COLUMN_NAME_COUNT, ShopListContract.Items.COLUMN_NAME_COUNT);


    }
    
    /**
    *
    * This class helps open, create, and upgrade the database file. Set to package visibility
    * for testing purposes.
    */
   private static class DatabaseHelper extends SQLiteOpenHelper {

       DatabaseHelper(Context context) {

           // calls the super constructor, requesting the default cursor factory.
           super(context, DATABASE_NAME, null, DATABASE_VERSION);
       }

       /**
        *
        * Creates the underlying database with table name and column names taken from the
        * NotePad class.
        */
       @Override
       public void onCreate(SQLiteDatabase db) {
           db.execSQL("CREATE TABLE " + ShopListContract.Items.TABLE_NAME + " ("
                   + ShopListContract.Items._ID + " INTEGER PRIMARY KEY,"
                   + ShopListContract.Items.COLUMN_NAME_NAME + " TEXT,"
                   + ShopListContract.Items.COLUMN_NAME_COUNT + " INTEGER"
                   + ");");
       }

       /**
        *
        * Demonstrates that the provider must consider what happens when the
        * underlying datastore is changed. In this sample, the database is upgraded the database
        * by destroying the existing data.
        * A real application should upgrade the database in place.
        */
       @Override
       public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

           // Logs that the database is being upgraded
           Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
                   + newVersion + ", which will destroy all old data");

           // Kills the table and existing data
           db.execSQL("DROP TABLE IF EXISTS notes");

           // Recreates the database with a new version
           onCreate(db);
       }
   }
    
	@Override
	public boolean onCreate() {
		// Creates a new helper object. Note that the database itself isn't
		// opened until
		// something tries to access it, and it's only created if it doesn't
		// already exist.
		mOpenHelper = new DatabaseHelper(getContext());

		// Assumes that any failures will be reported by a thrown exception.
		return true;
	}
	
	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
			String sortOrder) {
		
		// Constructs a new query builder and sets its table name
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		qb.setTables(ShopListContract.Items.TABLE_NAME);
		
		/**
		 * Choose the projection and adjust the "where" clause based on URI
		 * pattern-matching.
		 */
		switch (sUriMatcher.match(uri)) {
		// If the incoming URI is for notes, chooses the Notes projection
		case ITEMS:
			qb.setProjectionMap(sItemsProjectionMap);
			break;

		/*
		 * If the incoming URI is for a single note identified by its ID,
		 * chooses the note ID projection, and appends "_ID = <noteID>" to the
		 * where clause, so that it selects that single note
		 */
		case ITEM_ID:
			qb.setProjectionMap(sItemsProjectionMap);
			qb.appendWhere(ShopListContract.Items._ID + // the name of the ID
														// column
					"=" +
					// the position of the item ID itself in the incoming URI
					uri.getPathSegments().get(ShopListContract.Items.ITEM_ID_PATH_POSITION));
			break;
		case FILTER:
			qb.setProjectionMap(sItemsProjectionMap);
			String filter = uri.getLastPathSegment();
			if (!TextUtils.isEmpty(filter)) {
				qb.appendWhere(Items.COLUMN_NAME_NAME + " LIKE ");
                qb.appendWhereEscapeString('%' + filter + '%');
			}
			break;
		default:
			// If the URI doesn't match any of the known patterns, throw an
			// exception.
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
	       
		String orderBy;
		// If no sort order is specified, uses the default
		if (TextUtils.isEmpty(sortOrder)) {
			orderBy = ShopListContract.Items.DEFAULT_SORT_ORDER;
		} else {
			// otherwise, uses the incoming sort order
			orderBy = sortOrder;
		}
	       
		// Opens the database object in "read" mode, since no writes need to be
		// done.
		SQLiteDatabase db = mOpenHelper.getReadableDatabase();

		/*
		 * Performs the query. If no problems occur trying to read the database,
		 * then a Cursor object is returned; otherwise, the cursor variable
		 * contains null. If no records were selected, then the Cursor object is
		 * empty, and Cursor.getCount() returns 0.
		 */
		Cursor c = qb.query(db, // The database to query
				projection, // The columns to return from the query
				selection, // The columns for the where clause
				selectionArgs, // The values for the where clause
				null, // don't group the rows
				null, // don't filter by row groups
				orderBy // The sort order
				);

	       // Tells the Cursor what URI to watch, so it knows when its source data changes
	       c.setNotificationUri(getContext().getContentResolver(), uri);
	       return c;
	}
	
	@Override
	public String getType(Uri uri) {
		/**
		 * Chooses the MIME type based on the incoming URI pattern
		 */
		switch (sUriMatcher.match(uri)) {

		// If the pattern is for notes or live folders, returns the general
		// content type.
		case ITEMS:
			return ShopListContract.Items.CONTENT_TYPE;
		case ITEM_ID:
			return ShopListContract.Items.CONTENT_ITEM_TYPE;

			// If the URI pattern doesn't match any permitted patterns, throws
			// an exception.
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
	}
	
	@Override
	public Uri insert(Uri uri, ContentValues initialValues) {
		// Validates the incoming URI. Only the full provider URI is allowed for inserts.
        if (sUriMatcher.match(uri) != ITEMS) {
            throw new IllegalArgumentException("Unknown URI " + uri);
        }

        // A map to hold the new record's values.
        ContentValues values;

        // If the incoming values map is not null, uses it for the new values.
        if (initialValues != null) {
            values = new ContentValues(initialValues);

        } else {
        	throw new NullPointerException("ContentValues can not be null");
            // Otherwise, create a new value map
//            values = new ContentValues();
        }

        // Gets the current system time in milliseconds
        Long now = Long.valueOf(System.currentTimeMillis());

        // Opens the database object in "write" mode.
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();

        // Performs the insert and returns the ID of the new note.
		long rowId = db.insert(
				ShopListContract.Items.TABLE_NAME,
				ShopListContract.Items.COLUMN_NAME_NAME,
				values);

        // If the insert succeeded, the row ID exists.
        if (rowId > 0) {
            // Creates a URI with the note ID pattern and the new row ID appended to it.
            Uri noteUri = ContentUris.withAppendedId(ShopListContract.Items.CONTENT_ID_URI_BASE, rowId);

            // Notifies observers registered against this provider that the data changed.
            getContext().getContentResolver().notifyChange(noteUri, null);
            return noteUri;
        }

        // If the insert didn't succeed, then the rowID is <= 0. Throws an exception.
        throw new SQLException("Failed to insert row into " + uri);
	}
	
	@Override
	public int delete(Uri uri, String where, String[] whereArgs) {
		 // Opens the database object in "write" mode.
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        String finalWhere;

        int count;

        // Does the delete based on the incoming URI pattern.
        switch (sUriMatcher.match(uri)) {

		// If the incoming pattern matches the general pattern for items, does a
		// delete based on the incoming "where" columns and arguments.
		case ITEMS:
			count = db.delete(
					ShopListContract.Items.TABLE_NAME,
					where,
					whereArgs);
			break;

		// If the incoming URI matches a single item ID, does the delete based
		// on the incoming data, but modifies the where clause to restrict it to
		// the particular note ID.
		case ITEM_ID:
			/*
			 * Starts a final WHERE clause by restricting it to the desired note
			 * ID.
			 */
			finalWhere = ShopListContract.Items._ID + // The ID column name
					" = " + // test for equality
					uri.getPathSegments(). // the incoming note ID
							get(ShopListContract.Items.ITEM_ID_PATH_POSITION);

			// If there were additional selection criteria, append them to the
			// final
			// WHERE clause
			if (where != null) {
				finalWhere = finalWhere + " AND " + where;
			}

			// Performs the delete.
			count = db.delete(ShopListContract.Items.TABLE_NAME,
					finalWhere,
					whereArgs
					);
			break;

            // If the incoming pattern is invalid, throws an exception.
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

		/*
		 * Gets a handle to the content resolver object for the current context,
		 * and notifies it that the incoming URI changed. The object passes this
		 * along to the resolver framework, and observers that have registered
		 * themselves for the provider are notified.
		 */
        getContext().getContentResolver().notifyChange(uri, null);

        // Returns the number of rows deleted.
        return count;
	}





	@Override
	public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
		// Opens the database object in "write" mode.
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        int count;
        String finalWhere;

        // Does the update based on the incoming URI pattern
        switch (sUriMatcher.match(uri)) {

		case ITEMS:

			// Does the update and returns the number of rows updated.
			count = db.update(ShopListContract.Items.TABLE_NAME,
					values,
					where,
					whereArgs
					);
			break;

            // If the incoming URI matches a single note ID, does the update based on the incoming
            // data, but modifies the where clause to restrict it to the particular note ID.
		case ITEM_ID:
			// From the incoming URI, get the note ID
			String noteId = uri.getPathSegments().get(ShopListContract.Items.ITEM_ID_PATH_POSITION);

			/*
			 * Starts creating the final WHERE clause by restricting it to the
			 * incoming note ID.
			 */
			finalWhere = ShopListContract.Items._ID + // The ID column name
					" = " + // test for equality
					noteId;

			// If there were additional selection criteria, append them to the
			// final WHERE
			// clause
			if (where != null) {
				finalWhere = finalWhere + " AND " + where;
			}

			// Does the update and returns the number of rows updated.
			count = db.update(ShopListContract.Items.TABLE_NAME,
					values,
					finalWhere,
					whereArgs
					);
			break;
		// If the incoming pattern is invalid, throws an exception.
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
        }
        
        getContext().getContentResolver().notifyChange(uri, null);

        return count;
	}

}
